package sample.syntaxparser;

import org.dreamwork.text.TextParser;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-9-15
 * Time: 17:59:54
 */
public class JavaParser extends TextParser {
    private String encoding = "utf-8";
    private String[] keywords = {
        "abstract", "boolean",    "break",  "byte",       "case",    "catch",     "char",     "class",  "const",  "continue", 
        "default",  "do",         "double", "else",       "extends", "final",     "finally",  "float",  "for",    "goto",
        "if",       "implements", "import", "instanceof", "int",     "interface", "long",     "native", "new",    "package",
        "private",  "protected",  "public", "return",     "short",   "static",    "strictfp", "super",  "switch", "synchronized",
        "this",     "throw",      "throws", "transient",  "try",     "void",      "volatile", "while",  "null"
    }, classes = {};

    private String keyword_begin = "<span class='keyword'>";
    private String default_class_begin = "<span class='default-class'>";
    private String comment_begin = "<span class='comment'>";
    private String string_begin = "<span class='string'>";
    private String end = "</span>";

    private int quot_char = -2;

    private Map<String, String> roles = new HashMap<String, String> ();

    {
        roles.put ("keyword", keyword_begin);
        roles.put ("defaultclass", default_class_begin);
        roles.put ("comment", comment_begin);
        roles.put ("string", string_begin);
    }

    public JavaParser (File file) throws IOException {
        super (file);
    }

    public JavaParser (URL url) throws IOException {
        super (url);
    }

    public JavaParser (InputStream in) throws IOException {
        super (in);
    }

    public JavaParser (InputStream in, String charset) throws IOException {
        super (in, charset);
    }

    public JavaParser (String src) {
        super (src);
    }

    public JavaParser (Reader reader) throws IOException {
        super (reader);
    }

    public void setRoles (List<Role> roles) {
        if (roles != null) for (Role r : roles) {
            this.roles.put (r.getName (), "<span class='" + r.getCssClass () + "'>");
        }
    }

    public StringBuilder parse () {
        Segment s = new Segment (cursor, 0);
        StringBuilder ret = new StringBuilder ();

        keyword_begin = roles.get ("keyword");
        default_class_begin = roles.get ("defaultclass");
        comment_begin = roles.get ("comment");
        string_begin = roles.get ("string");

        for (int ch = nextChar(); ch != -1; ch = nextChar()) {
            if (ch == '/' || ch == '"' || ch == '\'') {
                switch (ch) {
                    case '/' :
                        if (stream [ cursor ] == '/') { // 行注释开始
							int temp = this.cursor - 1;
                            s = skipUntil ("\n");
							if (s != null) {
								s.start --;
								s.length += 2;
                                ret.append (comment_begin).append (escapeText (s.toString ())).append (end);
								this.reset (s);
							} else {
								String str = new String (stream, temp, stream.length - 1 - temp);
                                ret.append (comment_begin).append (escapeText (str)).append (end);
							}
                        } else if (stream [cursor] == '*') { // 块注释开始
                            s = this.skipUntil ("*/");
                            s.start --;
                            s.length += 3;
                            ret.append (comment_begin).append (escapeText (s.toString ())).append (end);
                            this.reset (s);
                        }
                        break;
					case '\'':
                    case '"':
						quot_char = ch;
                        s = this.skipUntilOutQuot ();
						if (s == null) return ret;
                        s.start --;
                        s.length ++;
                        ret.append (string_begin).append (escapeText (s.toString ())).append (end);
						this.reset (s);
                        break;
                }
            } else if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '$')) {
                s.length = cursor - s.start - 1;
                String word = s.toString ();
                if (isKeyWord (word))
                    ret.append (keyword_begin).append (word).append (end);
				else if (isDefaultClass (word))
                    ret.append (default_class_begin).append (word).append (end);
                else
                    ret.append (word);
				switch (ch) {
					case '\t' : ret.append ("    "); break;
					case '<'  : ret.append ("&lt;"); break;
					case '>'  : ret.append ("&gt;"); break;
					case '&'  : ret.append ("&amp;"); break;
					default   : ret.append ((char)ch);
				}
                this.reset (s);
            }
        }

		if (s != null) {
			s.length = this.cursor - s.start;
			if (s.length > 0)  ret.append (s);
		}
		return ret;        
    }

	private String escapeText (String text) {
		String ret = "";
		for (int i = 0; i < text.length (); i ++) {
			char ch = text.charAt (i);
			switch (ch) {
				case '\t' : ret += "    "; break;
				case '<'  : ret += "&lt;"; break;
				case '>'  : ret += "&gt;"; break;
				case '&'  : ret += "&amp;"; break;
				default   : ret += ch;
			}
		}
		return ret;
	}

    private Segment skipUntilOutQuot () {
        Segment seg = new Segment (this.cursor, 0);
        for (int ch = nextChar (); ch != -1; ch = nextChar ()) {
			if (quot_char == '"') {
				if (ch == '"' && stream [this.cursor - 2] != '\\') {
					seg.length = this.cursor - seg.start;
					quot_char = -2;
					return seg;
				}
			} else if (quot_char == '\'') {
				if (ch == '\'' && !(stream [cursor - 2] == '\\' && stream [cursor - 1] == '\\')) {
					seg.length = this.cursor - seg.start;
					quot_char = -2;
					return seg;
				}
			}
        }
        return null;
    }

	private boolean isKeyWord(String text) {
        for (String keyword : keywords) {
            if (keyword.equals (text)) return true;
        }

		return false;
	}

	private boolean isDefaultClass(String text) {
        for (String aClass : classes) {
            if (aClass.equals (text)) return true;
        }

		return false;
	}
}
